﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using ULSDeobfuscator;

namespace Elca.MvvmHelpers {
    public class VerticalDrawing : Grid {

        private const int ItemsCount = 100;

        private IList<AnnotationInfo> _infos;

        public static readonly DependencyProperty ItemsSourceProperty = 
            ItemsControl.ItemsSourceProperty.AddOwner(typeof(VerticalDrawing), 
                                                      new PropertyMetadata(null, OnItemsSourceChanged));

        public static readonly DependencyProperty CommandProperty = ButtonBase.CommandProperty.AddOwner(typeof(VerticalDrawing));

        public static readonly DependencyProperty EditCommandProperty = 
            DependencyProperty.Register("EditCommand", typeof(ICommand), typeof(VerticalDrawing));
        public static readonly DependencyProperty DeleteCommandProperty = 
            DependencyProperty.Register("DeleteCommand", typeof(ICommand), typeof(VerticalDrawing));

        public IList<AnnotationInfo> ItemsSource {
            get {
                return (IList<AnnotationInfo>) GetValue(ItemsSourceProperty);
            }
            set {
                SetValue(ItemsSourceProperty, value);
            }
        }

        public ICommand Command {
            get {
                return (ICommand) GetValue(CommandProperty);
            }
            set {
                SetValue(CommandProperty, value);
            }
        }

        public ICommand EditCommand {
            get {
                return (ICommand) GetValue(EditCommandProperty);
            }
            set {
                SetValue(EditCommandProperty, value);
            }
        }

        public ICommand DeleteCommand {
            get {
                return (ICommand) GetValue(DeleteCommandProperty);
            }
            set {
                SetValue(DeleteCommandProperty, value);
            }
        }

        static VerticalDrawing() {
            PropertyChangedCallback p = OnIsEnabledChanged;

            IsEnabledProperty.OverrideMetadata(
                typeof(VerticalDrawing),
                new FrameworkPropertyMetadata(OnIsEnabledChanged, OnCoerceIsEnabled));
        }

        private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
        }

        private static object OnCoerceIsEnabled(DependencyObject d, object baseValue) {
            return true;
        }

        public VerticalDrawing() {

            var oneStar = new GridLength(1, GridUnitType.Star);
            for (int i = 0; i < ItemsCount; i++) {
                RowDefinitions.Add(new RowDefinition { Height = oneStar});
            }
        }

        private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {

            VerticalDrawing vd = d as VerticalDrawing;
            if (vd == null) {

                throw new InvalidOperationException("I don't get it");
            }

            vd.Unloaded -= OnUnloaded;
            vd.Unloaded += OnUnloaded;

            if (e.OldValue != null) {
                vd.UnBindToData();                    
            }
            IList<AnnotationInfo> data = e.NewValue as IList<AnnotationInfo>;
            if (data != null) {
                vd.BindToData(data);
            }
        }

        private void BindToData(IList<AnnotationInfo> data) {

            if (data != null) {

                _infos = data;

                INotifyCollectionChanged ncc = data as INotifyCollectionChanged;
                if (ncc != null) {

                    ncc.CollectionChanged += OnSourceCollectionChanged;
                }
                ReplaceAllItems();
            }
        }

        private void UnBindToData() {
            INotifyCollectionChanged ncc = _infos as INotifyCollectionChanged;
            if (ncc != null) {

                ncc.CollectionChanged -= OnSourceCollectionChanged;
            }
        }

        private void OnSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {

            switch (e.Action) {
                    
                case NotifyCollectionChangedAction.Add:
                    AddItems(e.NewItems);
                    break;

                case NotifyCollectionChangedAction.Remove:
                    RemoveItems(e.OldItems);
                    break;
                case NotifyCollectionChangedAction.Move:
                    // don't care
                    break;

                case NotifyCollectionChangedAction.Replace:
                case NotifyCollectionChangedAction.Reset:
                    ReplaceAllItems();
                    break;

            }
        }

        private void ReplaceAllItems() {
            
            Children.Clear();
            AddItems(_infos);
        }

        private void OnEditCommandSelect(object parameter) {

            AnnotationInfo ai = (AnnotationInfo) parameter;

            ExecuteCommand(ai, EditCommand);
        }
        private void OnDeleteCommandSelect(object parameter) {

            AnnotationInfo ai = (AnnotationInfo) parameter;

            ExecuteCommand(ai, DeleteCommand);
        }

        private void AddItems(IEnumerable infos) {
            if (infos == null) {
                return;
            }
            foreach (AnnotationInfo ai in infos) {

                FrameworkElement ell = new Rectangle {
                    Tag = ai,
                    HorizontalAlignment = HorizontalAlignment.Left,
                    Fill = ai.Brush,
                    RadiusY = 2,
                    RadiusX = 2,
                    Opacity = 0.5,
                };

                // The following XAML was made in Blend with the help of http://wpf.2000things.com/2012/05/17/560-using-a-radial-gradient-to-create-a-3d-effect/
                // <RadialGradientBrush Center = "0.71,0.311" GradientOrigin = "0.669,0.349" RadiusY = "0.719" RadiusX = "0.7" >
                //    <GradientStop Color = "White" />
                //    <GradientStop Color = "#FF227C1F" Offset = "0.674" />
                //    <GradientStop Color = "#FF0E450C" Offset = "0.941" />
                // </ RadialGradientBrush >

                //GradientStopCollection stops = new GradientStopCollection {
                //    new GradientStop(Colors.White, 0),
                //    new GradientStop(ai.LightColor, 0.674),
                //    new GradientStop(ai.DarkColor, 0.941),
                //};
                //RadialGradientBrush brush = new RadialGradientBrush(stops) {
                //    Center = new Point(0.71, 0.311),
                //    RadiusX = 0.7,
                //    RadiusY = 0.719
                //};
                //ell.Fill = brush;

                if (ai.IsUserAnnotation) {

                    ell.ContextMenu = new ContextMenu();

                    MenuItem miEdit = new MenuItem {
                        Header = "Annotate...",
                        Command = new ExecutableCommand(OnEditCommandSelect),
                        CommandParameter = ai,
                        Icon = new Image {
                            Source = new BitmapImage(new Uri("Images/Annotate16.png", UriKind.Relative))
                        },
                    };
                    ell.ContextMenu.Items.Add(miEdit);

                    MenuItem miDelete = new MenuItem {
                        Header = "Delete annotation",
                        Command = new ExecutableCommand(OnDeleteCommandSelect),
                        CommandParameter = ai,
                        ContextMenu = new ContextMenu(),
                        Icon = new Image {
                            Source = new BitmapImage(new Uri("Images/Delete16.png", UriKind.Relative))
                        },
                    };
                    ell.ContextMenu.Items.Add(miDelete);
                }

                Binding widthBinding = new Binding("ActualHeight") {
                    Source = ell,
                };
                ell.SetBinding(WidthProperty, widthBinding);
                Binding tipBinding = new Binding("Text") {
                    Source = ai
                };
                ell.SetBinding(ToolTipProperty, tipBinding);
                Binding visibilityBinding = new Binding("IsVisible") {
                    Source = ai,
                    Converter = (IValueConverter) FindResource("Bool2VisibilityConverter"),
                };
                ell.SetBinding(VisibilityProperty, visibilityBinding);

                ell.MouseLeftButtonDown += OnAnnotationClick;
                Children.Add(ell);
                Binding rowBinding = new Binding("StartPercent") {
                    Source = ai,
                };
                ell.SetBinding(RowProperty, rowBinding);
            }
        }

        private void OnAnnotationClick(object sender, MouseButtonEventArgs e) {

            FrameworkElement rect = (FrameworkElement) sender;
            AnnotationInfo ai = (AnnotationInfo) rect.Tag;
            ExecuteCommand(ai, Command);
        }

        private void ExecuteCommand(AnnotationInfo ai, ICommand cmd) {

            LogEntry le = ai?.AssociatedEntry;

            if (le == null) {
                return;
            }
            if (cmd != null && cmd.CanExecute(le)) {
                cmd.Execute(le);
            }
        }

        private void RemoveItems(IEnumerable infos) {

            if (infos == null) {
                return;
            }

            foreach (AnnotationInfo vi in infos) {

                foreach (FrameworkElement rect in Children) {

                    if (rect.Tag == vi) {
                        Children.Remove(rect);
                        break;
                    }
                }
            }
        }

        private static void OnUnloaded(object sender, RoutedEventArgs e) {
            
            VerticalDrawing vd = sender as VerticalDrawing;
            if (vd == null) {

                throw new InvalidOperationException("I don't get it again");
            }

            vd.UnBindToData();
        }

    }
}
